Android实现倒计时的几种方案汇总 |
您所在的位置:网站首页 › 倒计时 动态 › Android实现倒计时的几种方案汇总 |
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情 前言关于倒计时可以说我们App开发中常见的一种场景了,比如Splash倒计时跳转首页,比如发送短信之后倒计时60秒显示等等。 关于倒计时的实现方式,大家可能有不同的做法,这里做一下总结看看你使用的是哪一种呢? 一、CountDownTimer的实现直接上代码 //倒计时的方式一 fun countDownTimer() { var num = 60 timer = object : CountDownTimer((num + 1) * 1000L, 1000L) { override fun onTick(millisUntilFinished: Long) { YYLogUtils.w("当时计数:" + num) if (num == 0) { YYLogUtils.w("重新开始") num = 60 } else { num-- } } override fun onFinish() { YYLogUtils.w("倒计时结束了..." + num) } } timer?.start() } private var timer: CountDownTimer? = null override fun onDestroy() { super.onDestroy() timer?.cancel() } 复制代码没什么花活,就是android.os包下面的 CountDownTimer 类的使用。内部实现使用了 Handler 进行封装。 二、直接用Handler的实现 private var handlerNum = 60 private val mHandler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { 1 -> { if (handlerNum > 0) { handlerNum-- YYLogUtils.w("当时计数:" + handlerNum) countDownHander() } else { stopCountDownHander() } } } } } override fun onDestroy() { super.onDestroy() stopCountDownHander() } fun countDownHander() { mHandler.sendEmptyMessageDelayed(1, 1000) } fun stopCountDownHander() { mHandler.removeCallbacksAndMessages(null) } 复制代码我们可以直接使用Handler的延时发送消息实现倒计时。 当然另一种做法是使用 Runnable 来实现。 Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { recLen++; txtView.setText("" + recLen); handler.postDelayed(this, 1000); } public void test(){ handler.postDelayed(runnable, 1000); } 复制代码 三、直接用Time、TimeTask的实现以上是Android的倒计时方案,其实Java的Api也是支持倒计时实现的,比如 Timer 配合 TimerTask 就可以实现简单的倒计时。 fun countDownTimer2() { var num = 60 val timer = Timer() val timeTask = object : TimerTask() { override fun run() { num-- YYLogUtils.w("当时计数:" + num) if (num < 0) { timer.cancel() } } } timer.schedule(timeTask, 1000, 1000) } 复制代码 四、使用Theard倒计时我们可以通过Thread的sleep方法来实现倒计时,不过由于是子线程我们不能更新UI,所以还是需要配合Handler实现。 private var mThread: Thread = Thread(this) private var mflag = false private var mThreadNum = 60 override fun run() { while (mflag && mThreadNum >= 0) { try { Thread.sleep(1000) } catch (e: InterruptedException) { e.printStackTrace() } val message = Message.obtain() message.what = 1 message.arg1 = mThreadNum handler.sendMessage(message) mThreadNum-- } } private val handler = Handler(Looper.getMainLooper()) { msg -> if (msg.what == 1) { val num = msg.arg1 //由于需要主线程显示UI,这里使用Handler通信 YYLogUtils.w("当时计数:" + num) } true } //开启倒计时 fun countDownThread() { if (!mThread.isAlive) { mflag = true if (mThread.state == Thread.State.TERMINATED) { mThread = Thread(this@DemoCountDwonActivity) if (mThreadNum == -1) mThreadNum = 60 mThread.start() } else { mThread.start() } } else { mflag = false } } override fun onDestroy() { super.onDestroy() mflag = false } 复制代码这里的销毁线程我没有使用stop方法,已经不推荐我们使用,我们使用flag来判断即可。 五、使用框架RxJava这样的线程并不是我们想要的,我们通常并不会直接new Thread 来进行一些逻辑操作,比如我们可能使用RxJava框架,通过操作符的方式来进行倒计时。 比我们倒计时4秒之后跳转页面的实现: val SHOTDOWN_TIME = 4 val mDisposables : Disposable? = null Observable.interval(0, 1, TimeUnit.SECONDS) .take(SHOTDOWN_TIME.toLong()) .map { return@map SHOTDOWN_TIME - it } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ LogUtil.e(it.toString()) }, { it.printStackTrace() }, { checkJump() }, { mDisposable = it }) override fun onDestroy() { super.onDestroy() mDisposable?.dispose() } 复制代码注意我们还是需要通过mDisposable对象在页面销毁的时候释放,以免内存泄露,有没有简单一点方式? 六、Kotlin Flow 的实现上面的方法都需要销毁资源,好麻烦,能不能自动取消?协程不就行了。 是的 lifecycleScope 根据生命周期自动取消的协程作用域,配合Flow的操作符完成倒计时岂不是完美。 好吧,你是自动倒计时了。结束之后取消协程,销毁也能取消协程,那如果我想手动的取消倒计时怎么办?比如倒计时60秒我就要在第50秒的时候强制取消协程怎么办? launch方法返回的不就是Job 对象吗?根据此上下文对象不就可以取消协程了吗? 看看灵活的Flow倒计时如何实现。 定义一个扩展方法: /** * 倒计时的实现 */ @ExperimentalCoroutinesApi fun FragmentActivity.countDown( time: Int = 5, start: (scop: CoroutineScope) -> Unit, end: () -> Unit, next: (time: Int) -> Unit ) { lifecycleScope.launch { // 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消 flow { (time downTo 0).forEach { delay(1000) emit(it) } }.onStart { // 倒计时开始 ,在这里可以让Button 禁止点击状态 start(this@launch) }.onCompletion { // 倒计时结束 ,在这里可以让Button 恢复点击状态 end() }.catch { //错误 YYLogUtils.e(issage ?: "Unkown Error") }.collect { // 在这里 更新值来显示到UI next(it) } } } 复制代码使用: fun startCountDown() { var timeDownScope: CoroutineScope? = null countDown( time = 60, start = { timeDownScope = it YYLogUtils.e("开始") }, end = { YYLogUtils.e("结速倒计时") toast("结速倒计时") }, next = { YYLogUtils.w("当时计数:" + it) if (it == 50) { timeDownScope?.cancel() } }) } 复制代码无需onDestory中销毁资源,如果想自由手动的控制倒计时,我们再start的高阶函数中接收父协程的上下文对象即可自动控制。使用起来也是超级简单。 总结倒计时的实现是我们常用的功能,如果你的项目是Kotlin构建的,那么我建议使用Flow来实现这种功能,使用扩展函数进行封装,使用起来更加的简单。 如果你们项目是Java语言实现的,那么同样的可以选择一种方式进行一个工具类的封装,也能达到同样的效果,只是记得需要在onDestory中销毁资源哦。 如有错漏还望指出,如有更多或更好的方法,也可以在评论区交流讨论。 如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。 Ok,这一期就此完结。 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |